-
Notifications
You must be signed in to change notification settings - Fork 372
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
move IAMRole and IAMRolePolicyAttachment to v1beta1 #141
Conversation
c19b3a4
to
e7cfb89
Compare
For the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking really good so far @sahil-lakhwani! Added a few comments, let me know if you need any additional context :)
@@ -57,7 +58,7 @@ func TestMain(m *testing.M) { | |||
func Test_Connect(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we refactor these tests to match our table-driven pattern in other v1beta1
resources?
@@ -57,7 +57,7 @@ func TestMain(m *testing.M) { | |||
func Test_Connect(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment here about table-driven tests.
pkg/clients/iam/iamrole.go
Outdated
return m | ||
} | ||
|
||
// UpdateRoleExternalStatus updates the external status object, given the observation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having External
in the name here as we are actually updating the name of the kubernetes object rather than the external IAM role on AWS. We typically defer this type of functionality to the controller, so this can likely be removed (see comments in controller).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead we would probably want something like a GenerateObservation
function, ref: https://github.com/crossplane/stack-aws/blob/94baed407fc79f3a083c409b42542a00f7f0eb74/pkg/clients/rds/rds.go#L220
return m | ||
} | ||
|
||
// UpdateRolePolicyExternalStatus updates the external status object, given the observation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same comment as in IAM role client.
|
||
result, err := req.Send(ctx) | ||
if err != nil { | ||
return managed.ExternalCreation{}, errors.Wrap(err, errCreate) | ||
} | ||
|
||
cr.UpdateExternalStatus(*result.Role) | ||
iam.UpdateRoleExternalStatus(cr, *result.Role) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We typically would update the status in the subsequent Observe
.
@@ -127,33 +127,31 @@ func (e *external) Observe(ctx context.Context, mgd resource.Managed) (managed.E | |||
|
|||
cr.SetConditions(runtimev1alpha1.Available()) | |||
|
|||
cr.UpdateExternalStatus(*attachedPolicyObject) | |||
iam.UpdateRolePolicyExternalStatus(cr, *attachedPolicyObject) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here we are already updating the status in the Observe
method (contrary to the iam role controller), but it would be nice to have a GenerateObservation
function that returns the AtProvider
observation rather than passing in the object to have its AtProvider
status set in the client.
fe284fb
to
f50bd6c
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is getting very close! Thank you for continuing to push through on this @sahil-lakhwani! Main comments are around adding a few additional fields to the IAMRole
spec. Let me know if you have any questions! :)
@@ -38,13 +34,13 @@ type IAMRoleParameters struct { | |||
Description string `json:"description,omitempty"` | |||
|
|||
// RoleName presents the name of the IAM role. | |||
RoleName string `json:"roleName"` | |||
// RoleName string `json:"roleName"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can go ahead and remove this :)
@@ -38,13 +34,13 @@ type IAMRoleParameters struct { | |||
Description string `json:"description,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Description string `json:"description,omitempty"` | |
Description *string `json:"description,omitempty"` |
@sahil-lakhwani What is the latest on updating and merging this PR? |
Hi @infinitecompute |
|
||
// The key name that can be used to look up or retrieve the associated value. | ||
// For example, Department or Cost Center are common choices. | ||
Key string `json:"key,omitempty"` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is required per https://docs.aws.amazon.com/IAM/latest/UserGuide/id_tags.html so you can remove omitempty
:
"You can create a tag with an empty value such as phoneNumber = . You cannot create an empty tag key."
return managed.ExternalUpdate{}, errors.Wrapf(err, errDetach, cr.Status.AttachedPolicyARN, cr.Spec.RoleName) | ||
} | ||
|
||
// PolivyARN is the only distinguishing field and on update to that, new policy is attached |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is PolicyARN
actually an immutable field? If so, I think we can skip doing anything in Update
.
Letting the user know about the drift is challenging in AWS because update requests are constructed only with update-able parameters, hence API server wouldn't return an error like GCP this field cannot be updated
. Ideally, validation should reject editing an immutable field.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@hasheddan @muvaf In the current implementation, if the PolicyARN
in spec
is changed anyhow, a new policy is being created. And in the Update
method, we have:
if cr.Status.AttachedPolicyARN == "" || cr.Spec.PolicyARN == cr.Status.AttachedPolicyARN
I think we can check for the above condition in Observe
itself, and decide either to create a new policy or update the existing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sahil-lakhwani I am hesitant to delete and recreate an external object as part of the update logic of a reconciler because it feels like it violates that contract of the lifecycle of a custom resource being tied to the lifecycle of an external resource. @muvaf let me know if you think differently here. It sounds like the path forward for the time being is to just always return true
for ResourceUpToDate
if the IAMRolePolicyAttachment
exists at all (per @muvaf comment), and keep the current functionality you have here of doing nothing in the Update
method.
@muvaf I wonder if we should expose something in crossplane-runtime
that allows consumers to report back that "this resource is not up to date, but we are not trying to fix it". We could do this currently by manually updating status condition here, but I think crossplane-runtime
would overwrite it after we returned an empty managed.ExternalUpdate
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah we don't delete anything unless actual deletion request comes in. @sahil-lakhwani this field should be marked as immutable and when we get around implementation of immutability, it will work. But for the time being, that's all we can do.
About the drift in general; there are three kinds of drifts:
- In progress: you changed something and the update process is going on but the property is still the same in external resource, like you increased the size of DB from 20GB to 400GB and
spec
says 400GB immediately while the process is going on. Not much to do here unless provider reports an unavailable state, then we could update the condition. But other than that, status is expected to show that an update is going on if possible. - Immutable field changed by user. Specifically in AWS,
Modify
requests do not include those fields so we can't have provider return error in those cases. The best we could do is to prevent those fields from being edited. - Update-able field changed but resulted in error. Provider tells you the error and we show that.
@hasheddan For the second case, we could investigate a mechanism where in Observe
we compare spec
with what exists in the provider and update a condition
saying whether there is a drift without going into details like whether this was because an immutable field or an update is due etc. It's a bit challenging to implement this in a good way though (comparison is hard). Maybe use IsUpToDate check to set a condition? But that'll require full scan of fields opposed to selective scanning that current IsUpToDate
functions do.
|
||
_, err := req.Send(ctx) | ||
|
||
return managed.ExternalCreation{}, errors.Wrapf(err, errAttach, cr.Spec.PolicyARN, cr.Spec.RoleName) | ||
return managed.ExternalCreation{}, errors.Wrapf(err, errAttach, cr.Spec.ForProvider.PolicyARN, cr.Spec.ForProvider.RoleName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You don't need to return anything other than the error(err
) and the error string(errAttach
) as managed reconciler properly fills the error and logs with necessary identifiers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@muvaf errAttach
has format specifiers, and removing the values for those resulted in this for me (kubectl describe)
Warning CannotCreateExternalResource 6m59s managed/iamrolepolicyattachment.identity.aws.crossplane.io failed to attach the policy with arn %!!(MISSING)v(MISSING) to role %!!(MISSING)v(MISSING): NoSuchEntity: Policy arn:aws:iam::aws:policy/AleaForBusinessDeviceSetup does not exist or is not attachable.
The values are not filled and I am not sure how they will. Am I missing something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe @muvaf just means that you can remove the formatted string and return errors.Wrap(err, errAttach)
:)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return managed.ExternalCreation{}, errors.Wrapf(err, errAttach, cr.Spec.ForProvider.PolicyARN, cr.Spec.ForProvider.RoleName) | |
return managed.ExternalCreation{}, errors.Wrap(err, errAttach) |
I'm suggesting this because this. We don't need to repeat what's on the CR as long as the error string is specific enough.
return managed.ExternalUpdate{}, errors.Wrapf(err, errDetach, cr.Status.AttachedPolicyARN, cr.Spec.RoleName) | ||
} | ||
|
||
// PolivyARN is the only distinguishing field and on update to that, new policy is attached |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is PolicyARN
actually an immutable field? If so, I think we can skip doing anything in Update
.
Letting the user know about the drift is challenging in AWS because update requests are constructed only with update-able parameters, hence API server wouldn't return an error like GCP this field cannot be updated
. Ideally, validation should reject editing an immutable field.
return managed.ExternalUpdate{}, errors.Wrapf(err, errDetach, cr.Status.AttachedPolicyARN, cr.Spec.RoleName) | ||
} | ||
|
||
// PolivyARN is the only distinguishing field and on update to that, new policy is attached |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sahil-lakhwani I am hesitant to delete and recreate an external object as part of the update logic of a reconciler because it feels like it violates that contract of the lifecycle of a custom resource being tied to the lifecycle of an external resource. @muvaf let me know if you think differently here. It sounds like the path forward for the time being is to just always return true
for ResourceUpToDate
if the IAMRolePolicyAttachment
exists at all (per @muvaf comment), and keep the current functionality you have here of doing nothing in the Update
method.
@muvaf I wonder if we should expose something in crossplane-runtime
that allows consumers to report back that "this resource is not up to date, but we are not trying to fix it". We could do this currently by manually updating status condition here, but I think crossplane-runtime
would overwrite it after we returned an empty managed.ExternalUpdate
.
@hasheddan @muvaf I have added new commits to address the comments. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe there are a few more of @muvaf comments to address, but going to go ahead and give this the LGTM! @sahil-lakhwani would you mind running a few manual e2e tests (like you did earlier) to verify functionality is operating as expected? Thanks so much for continuing to push through this!
@@ -81,92 +88,97 @@ func (conn *connector) Connect(ctx context.Context, mgd resource.Managed) (manag | |||
if err != nil { | |||
return nil, errors.Wrap(err, errClient) | |||
} | |||
return &external{c}, nil | |||
return &external{c, conn.client}, nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No change needed for now, but I generally prefer to include the names of fields in the struct being returned for readability and new contributors :)
|
||
_, err := req.Send(ctx) | ||
|
||
return managed.ExternalCreation{}, errors.Wrapf(err, errAttach, cr.Spec.PolicyARN, cr.Spec.RoleName) | ||
return managed.ExternalCreation{}, errors.Wrapf(err, errAttach, cr.Spec.ForProvider.PolicyARN, cr.Spec.ForProvider.RoleName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe @muvaf just means that you can remove the formatted string and return errors.Wrap(err, errAttach)
:)
@hasheddan Couldn't directly reply to your comment #141 (comment) but the thing you mentioned has been taken care of: Also, I saw the test coverage results today, will take a look. |
Is additional code coverage the only blocker to merging? |
} | ||
|
||
return managed.ExternalObservation{}, errors.Wrapf(err, errGet, cr.Spec.RoleName) | ||
return managed.ExternalObservation{}, errors.Wrapf(resource.Ignore(iam.IsErrorNotFound, err), errGet, cr.Spec.ForProvider.RoleName) | ||
} | ||
|
||
var attachedPolicyObject *awsiam.AttachedPolicy | ||
for i := 0; i < len(observed.AttachedPolicies); i++ { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for i := 0; i < len(observed.AttachedPolicies); i++ { | |
for _, policy := range observed.AttachedPolicies { | |
if cr.Spec.ForProvider.PolicyARN == aws.StringValue(policy.PolicyArn) { | |
attachedPolicyObject = &policy | |
break | |
} | |
} |
Nitpick: using range
would probably be easier to read.
|
||
_, err := req.Send(ctx) | ||
|
||
return managed.ExternalCreation{}, errors.Wrapf(err, errAttach, cr.Spec.PolicyARN, cr.Spec.RoleName) | ||
return managed.ExternalCreation{}, errors.Wrapf(err, errAttach, cr.Spec.ForProvider.PolicyARN, cr.Spec.ForProvider.RoleName) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return managed.ExternalCreation{}, errors.Wrapf(err, errAttach, cr.Spec.ForProvider.PolicyARN, cr.Spec.ForProvider.RoleName) | |
return managed.ExternalCreation{}, errors.Wrap(err, errAttach) |
I'm suggesting this because this. We don't need to repeat what's on the CR as long as the error string is specific enough.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assuming you tested this manually, it looks good to me.
However, I think we might want to hold off merging this or maybe marking it v1alpha4
until we have a good idea about #157 because the discussion might lead to deletion of IAMRolePolicyAttachment
CRD, which is much harder to do if it's v1beta1
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for driving this @sahil-lakhwani! It's looking very good. Happy to merge once the remaining comments are addressed.
@infinitecompute Our code coverage check is currently aspirational. Ideally PRs would not lower the overall project coverage, but if the PR author and reviewers agree that there's little value in additional PR coverage we can merge. Based on a quick glance at https://codecov.io/gh/crossplane/provider-aws/pull/141/tree I'm not too concerned about any of this functionality being dramatically undertested.
pkg/controller/identity/iamrolepolicyattachment/controller_test.go
Outdated
Show resolved
Hide resolved
pkg/controller/identity/iamrolepolicyattachment/controller_test.go
Outdated
Show resolved
Hide resolved
Signed-off-by: sahil-lakhwani <sahilakhwani@gmail.com>
Signed-off-by: sahil-lakhwani <sahilakhwani@gmail.com>
Signed-off-by: sahil-lakhwani <sahilakhwani@gmail.com>
Signed-off-by: sahil-lakhwani <sahilakhwani@gmail.com>
Signed-off-by: sahil-lakhwani <sahilakhwani@gmail.com>
Signed-off-by: sahil-lakhwani <sahilakhwani@gmail.com>
Signed-off-by: sahil-lakhwani <sahilakhwani@gmail.com>
Signed-off-by: sahil-lakhwani <sahilakhwani@gmail.com>
Signed-off-by: sahil-lakhwani <sahilakhwani@gmail.com>
Signed-off-by: sahil-lakhwani <sahilakhwani@gmail.com>
Signed-off-by: sahil-lakhwani <sahilakhwani@gmail.com>
Signed-off-by: sahil-lakhwani <sahilakhwani@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on conclusion in #157 this looks good to me for merge.
Signed-off-by: sahil-lakhwani <sahilakhwani@gmail.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @sahil-lakhwani! This looks good to me. I'm going to merge this once we've cut the 0.9 release branch, so this will be included in 0.10.
@negz I was trying to write the YAMLs for the scenario here and realized that we don't support inline IAM policies. Digging more, I found out I suggest:
The idea is that we'd have one managed resource for inline policy operations as opposed to mapping each imperative call to a managed resource. This functionality is additive and not blocking this PR but I wanted to drop it on this PR in case we want to rename the resource, better to do it before bumping to Also, should we drop |
This sounds reasonable to me. We'd have |
Are you sure this is the case? As far as I can tell there's both
This doesn't match my understanding of the API. There seems to be a
@muvaf do we need to support inline IAM policies to support that use case? Under the current model, could we:
Would this mean we could only attach IAM policies to IAM roles that were managed by Crossplane? It seems to me like there are two ways to associate a policy with a role; either include the policy inline, or add it as a distinct API object and create a reference between the two. The latter seems to be the more granular and flexible model. |
Sounds good. The rest of the discussions is not a blocker for this PR. I can prepare a PR to do that. There is this doc that explains the inline and standalone policies. In all cases, you can use standalone managed policies with any entity (group, role, user etc.). The inline policy has essentially the same semantics except it has a one-to-one relationship with the entity. Quoting from Inline Policies section of that doc:
What I meant by copying is the mechanism that is described in the doc: if we used So, I agree that managed policy attachment and inline policy addition should be different resources.
That's what we are not able to do at the moment and I figured if we were able to expand the attachment for inline policy, then it'd have been pretty easy to enable the use-case.
I agree. But both ways have their own use cases. In this instance, even though it's possible to create a standalone policy, doing it inline makes more sense since the policy directly refers to an RDS instance and is the encouraged way. So, at some point we want inline option, too. But I can do with normal managed policy for now. |
I mentioned this seemed like a good idea earlier, but now I'm thinking we can probably leave it as is. It seems like https://aws.amazon.com/iam/ is one service of https://aws.amazon.com/identity/, which also include SSO, Cognito, etc. I also suspect including IAM in the CR names will help folks, because you don't usually include the group in |
I have a feeling that identity is merely a marketing term. The grouping in the SDK seems like a better candidate to use in our groupings. You can see
Yeah, FWIW, you don't have to include the full group when there is collision. |
…ib#141) Signed-off-by: sahil-lakhwani <sahilakhwani@gmail.com>
…-timeout-annotation Fix Uptest timeout annotation
Description of your changes
Add implementations of IAMRole and IAMRolePolicyAttachment that adhere to the managed resources standards
Tested with the following
IAMRole
IAMRolePolicyAttament
Checklist
I have:
make reviewable
to ensure this PR is ready for review.app.yaml
to include any new role permissions.